package cn.redragon.soa.common.criteria;

import static org.springframework.data.domain.Sort.Order.asc;
import static org.springframework.data.domain.Sort.Order.desc;

import cn.redragon.soa.common.constant.Constants.Pagination;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class SearchCriteria implements Serializable {
    
    private static final long serialVersionUID = -2527438529860800493L;
    
    protected Map<String, Direction> sortMap = new LinkedHashMap<>();
    private PageRequest pageRequest;
    private Integer page;
    private Integer size;
    private Direction sortDir;
    private String sortBy;

    @JsonIgnore
    public Pageable getPageable() {
        int pageNumber = Math.max(this.getPage() - 1, 0);
        Map<String, Direction> sortMap = this.getSortMap();
        if (StringUtils.isNotBlank(sortBy)) {
            sortMap.put(sortBy, sortDir == null ? Direction.ASC : sortDir);
        }
        this.pageRequest = Optional.ofNullable(getSortMap())
            .filter(map -> map.size() > 0)
            .map(sort -> {
                List<Order> orders = getSortMap().entrySet().stream()
                    .map(entry -> entry.getValue() == Direction.ASC ? asc(entry.getKey()) : desc(entry.getKey()))
                    .collect(Collectors.toList());
                return PageRequest.of(pageNumber, this.getSize(), Sort.by(orders));
            }).orElseGet(() -> PageRequest.of(pageNumber, this.getSize(), Sort.by("id")));

        return this.pageRequest;
    }

    public Integer getPage() {
        return Optional.ofNullable(page).orElse((int) Pagination.DEFAULT_PAGE_NUMBER);
    }

    public Integer getSize() {
        return Optional.ofNullable(size).orElse((int) Pagination.DEFAULT_PAGE_SIZE);
    }
}